#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
完整的自动化解密流程：
1. 启动Frida Hook
2. 调用API触发SDK
3. 捕获密钥
4. 解密数据
"""

import frida
import sys
import time
import re
import requests
import json
import base64
import subprocess
import io

# 修复Windows编码问题
if sys.platform == 'win32':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

# 配置
API_URL = "http://120.27.155.222:9999/api/message/send-test"
PACKAGE_NAME = "com.mcdonalds.gma.cn"

# 读取JS脚本
print("📜 读取Hook脚本...")
with open('verify_rsa_key.js', 'r', encoding='utf-8') as f:
    js_code = f.read()

print("=" * 80)
print("🔐 完整解密流程 - 自动化执行")
print("=" * 80)

# 捕获的数据
captured_data = {
    'aes_key': None,
    'iv': None,
    'timestamp': None
}

all_messages = []

def on_message(message, data):
    """处理Frida消息"""
    global captured_data, all_messages
    
    if message['type'] == 'send':
        payload = message.get('payload', '')
        all_messages.append(payload)
        print(payload)
        
        # 提取AES密钥 - 多种模式
        if 'AES_set_encrypt_key' in payload or '密钥:' in payload:
            match = re.search(r'密钥:\s*([0-9a-fA-F]{64})', payload)
            if match:
                captured_data['aes_key'] = match.group(1)
                captured_data['timestamp'] = time.time()
                print(f"\n✅ 捕获到AES密钥: {captured_data['aes_key']}\n")
        
        # 提取IV
        if 'IV:' in payload:
            match = re.search(r'IV:\s*([0-9a-fA-F]{32})', payload, re.IGNORECASE)
            if match:
                captured_data['iv'] = match.group(1)
                print(f"✅ 捕获到IV: {captured_data['iv']}\n")
        
        # 从RSA加密的明文中提取
        if 'RSA_public_encrypt' in payload and '明文' in payload:
            lines = payload.split('\n')
            hex_data = []
            for line in lines:
                # 匹配十六进制行: 0000: xx xx xx xx ...
                if re.match(r'^\d{4}:', line):
                    hex_part = line.split(':', 1)[1]
                    if '#' in hex_part:
                        hex_part = hex_part.split('#')[0]
                    # 提取所有十六进制字节
                    hex_bytes = ''.join(hex_part.split())
                    hex_data.append(hex_bytes)
            
            if hex_data:
                full_hex = ''.join(hex_data)
                if len(full_hex) >= 96:  # 48字节 = 96个十六进制字符
                    captured_data['aes_key'] = full_hex[:64]  # 前32字节
                    captured_data['iv'] = full_hex[64:96]      # 后16字节
                    captured_data['timestamp'] = time.time()
                    print(f"\n✅ 从RSA明文提取密钥:")
                    print(f"   AES密钥: {captured_data['aes_key']}")
                    print(f"   IV: {captured_data['iv']}\n")
        
    elif message['type'] == 'error':
        print(f"❌ Frida错误: {message.get('stack', message)}")

def decrypt_aes_cbc(encrypted_b64, key_hex, iv_hex):
    """使用AES-CBC解密"""
    from Crypto.Cipher import AES
    from Crypto.Util.Padding import unpad
    
    # Base64解码
    encrypted_data = base64.b64decode(encrypted_b64)
    
    # 十六进制转字节
    key = bytes.fromhex(key_hex)
    iv = bytes.fromhex(iv_hex)
    
    # AES-CBC解密
    cipher = AES.new(key, AES.MODE_CBC, iv)
    decrypted = cipher.decrypt(encrypted_data)
    
    # 去除填充
    try:
        decrypted = unpad(decrypted, AES.block_size)
    except:
        pass  # 如果没有填充，继续
    
    return decrypted.decode('utf-8')

try:
    # 步骤1: 启动Frida Hook
    print("\n【步骤1】启动Frida Hook")
    print("=" * 80)
    
    print("📱 连接设备...")
    device = frida.get_usb_device()
    print(f"✓ 已连接: {device}\n")
    
    print(f"🚀 启动应用 {PACKAGE_NAME}...")
    try:
        device.kill(PACKAGE_NAME)
        time.sleep(1)
    except:
        pass
    
    pid = device.spawn([PACKAGE_NAME])
    session = device.attach(pid)
    print(f"✓ 应用已启动 (PID: {pid})\n")
    
    print("📜 加载Hook脚本...")
    script = session.create_script(js_code)
    script.on('message', on_message)
    script.load()
    print("✓ Hook脚本已加载\n")
    
    device.resume(pid)
    print("✓ 应用已恢复运行\n")
    
    print("✅ Frida Hook已激活！\n")
    
    # 等待应用启动
    print("⏳ 等待应用完全启动 (5秒)...")
    time.sleep(5)
    
    # 步骤2: 调用API
    print("\n【步骤2】调用API触发SDK")
    print("=" * 80)
    
    print(f"📡 调用API: {API_URL}")
    response = requests.get(API_URL, timeout=10)
    
    if response.status_code == 200:
        api_data = response.json()
        print("✓ API调用成功\n")
        print(json.dumps(api_data, indent=2, ensure_ascii=False))
        
        # 保存响应
        with open('api_response.json', 'w', encoding='utf-8') as f:
            json.dump(api_data, f, indent=2, ensure_ascii=False)
        
        # 提取加密数据
        encrypted_dev = api_data.get('dev', '')
        encrypted_box = api_data.get('last_box', '')
        
        print(f"\n加密数据:")
        print(f"  dev: {encrypted_dev[:50]}...")
        print(f"  last_box: {encrypted_box[:50]}...\n")
        
    else:
        print(f"❌ API调用失败: {response.status_code}")
        sys.exit(1)
    
    # 步骤3: 等待捕获密钥
    print("\n【步骤3】等待捕获密钥")
    print("=" * 80)
    
    print("⏳ 等待Hook捕获密钥 (最多30秒)...\n")
    
    start_time = time.time()
    timeout = 30
    
    while time.time() - start_time < timeout:
        time.sleep(1)
        
        if captured_data['aes_key'] and captured_data['iv']:
            print("\n✅ 密钥捕获成功！")
            break
        
        elapsed = int(time.time() - start_time)
        if elapsed % 5 == 0:
            print(f"⏱️  等待中... {elapsed}/{timeout}秒")
    
    if not (captured_data['aes_key'] and captured_data['iv']):
        print("\n⚠️  未能捕获到密钥")
        print(f"   接收到 {len(all_messages)} 条Frida消息")
        
        if len(all_messages) == 0:
            print("\n可能原因:")
            print("1. Hook脚本未能正确注入")
            print("2. SO库未加载")
            print("3. API调用未触发SDK")
        else:
            print("\n接收到了一些消息，但未找到密钥")
            print("请检查Hook脚本的匹配逻辑")
        
        sys.exit(1)
    
    # 步骤4: 解密数据
    print("\n【步骤4】解密数据")
    print("=" * 80)
    
    print(f"AES密钥: {captured_data['aes_key']}")
    print(f"IV: {captured_data['iv']}\n")
    
    # 保存密钥
    with open('captured_keys.txt', 'w', encoding='utf-8') as f:
        f.write(f"AES密钥: {captured_data['aes_key']}\n")
        f.write(f"IV: {captured_data['iv']}\n")
        f.write(f"捕获时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(captured_data['timestamp']))}\n")
    
    print("✓ 密钥已保存到 captured_keys.txt\n")
    
    try:
        print("🔓 解密 'dev' 字段...")
        decrypted_dev = decrypt_aes_cbc(encrypted_dev, captured_data['aes_key'], captured_data['iv'])
        
        print("\n" + "=" * 80)
        print("🎉 解密成功！")
        print("=" * 80)
        print(decrypted_dev)
        print("=" * 80)
        
        # 尝试解析为JSON
        try:
            dev_json = json.loads(decrypted_dev)
            print("\n📋 解析后的JSON:")
            print(json.dumps(dev_json, indent=2, ensure_ascii=False))
        except:
            pass
        
        # 保存解密结果
        with open('decrypted_result.txt', 'w', encoding='utf-8') as f:
            f.write("解密的 dev 字段:\n")
            f.write("=" * 80 + "\n")
            f.write(decrypted_dev + "\n")
            f.write("=" * 80 + "\n")
        
        print("\n✅ 解密结果已保存到 decrypted_result.txt")
        
        # 也尝试解密 last_box
        if encrypted_box:
            print("\n🔓 解密 'last_box' 字段...")
            try:
                decrypted_box = decrypt_aes_cbc(encrypted_box, captured_data['aes_key'], captured_data['iv'])
                print(f"✓ last_box 解密成功")
                print(decrypted_box[:200] + "...")
            except Exception as e:
                print(f"⚠️  last_box 解密失败: {e}")
        
    except Exception as e:
        print(f"\n❌ 解密失败: {e}")
        import traceback
        traceback.print_exc()
        
        print("\n尝试使用Java解密工具...")
        print("运行: java DecryptWithCapturedKey")
    
    print("\n" + "=" * 80)
    print("✅ 完成！")
    print("=" * 80)
    
    session.detach()
    
except KeyboardInterrupt:
    print("\n\n⚠️  用户中断")
except Exception as e:
    print(f"\n❌ 错误: {e}")
    import traceback
    traceback.print_exc()
    sys.exit(1)

